#ifndef __M_MATCHER_HPP__
#define __M_MATCHER_HPP__

#include <iostream>
#include <list>
#include <mutex>
#include <condition_variable>
#include <thread>
#include "db.hpp"
#include "online.hpp"
#include "room.hpp"

/*自定义一个阻塞队列，完成出队入队，任意位置删除等动作*/
template <class T>
class match_queue
{
public:
    /*获取队列中元素个数*/
    int size()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.size();
    }
    /*判断队列是否为空*/
    bool empty()
    {
        std::unique_lock<std::mutex> locK(_mutex);
        return _list.empty();
    }
    /*阻塞等待接口*/
    void wait()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);
    }
    /*入队并唤醒*/
    void push(const T &data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_back(data);
        _cond.notify_all();
    }
    /*出队*/
    bool pop(T &data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if (_list.empty())
            return false;
        data = _list.front();
        _list.pop_front();
        return true;
    }
    /*移除指定位置数据*/
    void remove(T &data)
    {
        std::unique_lock<std::mutex> locK(_mutex);
        _list.remove(data);
    }

private:
    std::list<T> _list;            // 基于双向链表的阻塞队列
    std::mutex _mutex;             // 加锁保护阻塞队列出队入队等操作
    std::condition_variable _cond; // 条件变量控制入队唤醒动作,保证其同步性
};

class matcher
{
private:
    void handler_match(match_queue<uint64_t> &mq)
    {
        while (true)
        {
            // 1.判断匹配队列中用户数量是否大于2，小于2则阻塞
            while (mq.size() < 2)
                mq.wait();
            // 2.走到这里证明匹配队列中人数大于2
            uint64_t uid1;
            uint64_t uid2;
            bool ret = mq.pop(uid1);
            if (ret == false)
                continue;
            ret = mq.pop(uid2);
            if (ret == false)
            {
                this->add(uid1);
                continue;
            }
            // 3.判断两个玩家是否在线，如果有人突然掉线了，则将另一个玩家添加进匹配队列
            wsserver_t::connection_ptr conn1 = _om->get_conn_from_hall(uid1);
            if (conn1.get() == nullptr)
            {
                this->add(uid2);
                continue;
            }
            wsserver_t::connection_ptr conn2 = _om->get_conn_from_hall(uid2);
            if (conn2.get() == nullptr)
            {
                this->add(uid1);
                continue;
            }
            // 4.为两位玩家创建游戏房间，并将玩家加入游戏房间中
            room_ptr rp = _rm->create_room(uid1, uid2);
            if (rp.get() == nullptr)
            {
                this->add(uid1);
                this->add(uid2);
                continue;
            }
            // 对两个玩家进行响应
            Json::Value resp;
            resp["optype"] = "match_success";
            resp["result"] = true;
            std::string body;
            json_util::serialize(resp, body);
            conn1->send(body);
            conn2->send(body);
        }
    }

    void th_normal_entry() { return handler_match(_q_normal); }
    void th_high_entry() { return handler_match(_q_high); }
    void th_super_entry() { return handler_match(_q_super); }

public:
    matcher(room_manager *rm, user_table *ut, online_manager *om)
        : _rm(rm), _ut(ut), _om(om), _th_normal(std::thread(&matcher::th_normal_entry, this)), _th_high(std::thread(&matcher::th_high_entry, this)), _th_super(std::thread(&matcher::th_super_entry, this))
    {
        DLOG("游戏匹配模块初始化完毕!!!");
    }

    /*添加进相应的匹配队列中*/
    bool add(uint64_t uid)
    {
        // 根据玩家对应的天梯分数，将玩家添加进对应的匹配队列中匹配
        // 1.根据用户id，获取用户信息
        Json::Value user;
        bool ret = _ut->select_by_id(uid, user);
        if (ret == false)
        {
            DLOG("获取玩家:%d 信息失败!!!", uid);
            return false;
        }
        int score = user["score"].asInt();
        // 2.根据天梯分数将玩家添加进不同的匹配队列中
        if (score < 2000)
            _q_normal.push(uid);
        else if (score >= 2000 && score < 3000)
            _q_high.push(uid);
        else
            _q_super.push(uid);
        return true;
    }

    bool del(uint64_t uid)
    {
        Json::Value user;
        bool ret = _ut->select_by_id(uid, user); // 通过uid获取用户信息
        if (ret == false)
        {
            DLOG("获取玩家:%d 信息失败", uid);
            return false;
        }
        int score = user["score"].asInt(); // 获取天梯分数，根据不同的天梯分数找到不同的匹配队列
        if (score < 2000)
            _q_normal.remove(uid);
        else if (score >= 2000 & score < 3000)
            _q_high.remove(uid);
        else
            _q_super.remove(uid);
        return true;
    }

private:
    match_queue<uint64_t> _q_normal; // 青铜水平匹配队列
    match_queue<uint64_t> _q_high;   // 白银水平匹配队列
    match_queue<uint64_t> _q_super;  // 黄金水平匹配队列
    room_manager *_rm;               // 游戏房间管理操作句柄
    user_table *_ut;                 // 数据库操作句柄
    online_manager *_om;             // 在线管理操作句柄
    std::thread _th_normal;          // 青铜水平执行线程
    std::thread _th_high;            // 白银水平执行线程
    std::thread _th_super;           // 黄金水平执行线程
};

#endif